package performance;

import lombok.*;

import java.util.*;
import java.util.stream.Collectors;

/**
 * HashMap 笔记：
 * 1. HashMap 添加元素：
 *    - `OnlyIfAbsent` 默认为 `false`，即只有当键相同时才会覆盖原值，否则使用 `putIfAbsent` 方法，仅覆盖空值。
 *    <br>
 * 2. HashMap 内部结构：
 *    - HashMap 采用数组 + 链表 / 红黑树（JDK 1.8 以后）存储数据。
 *    - 其 `Node<K,V>` 结构如下：
 *      ```java
 *      static class Node<K,V> implements Map.Entry<K,V> {
 *          final int hash;
 *          final K key;
 *          V value;
 *          Node<K,V> next;
 *          Node(int hash, K key, V value, Node<K,V> next) {
 *              this.hash = hash;
 *              this.key = key;
 *              this.value = value;
 *              this.next = next;
 *          }
 *      }
 *      ```
 *    - 其中 `hash` 为 key 计算得出的哈希值，用于快速索引。
 *    <br>
 * 3. HashMap putVal 逻辑：
 *    - 插入时，仅判断 key：
 *      - key 的 hash 值必须相同。
 *      - key 的内存地址相同，或者 `equals()` 方法返回 `true`。
 *      ```java
 *      if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
 *          e = p;
 *      ```
 *    - 这样可保证 key 唯一，避免数据冲突。
 *    <br>
 * 4. 高低位扰动算法：
 *    - 目的：减少哈希碰撞，提高 HashMap 查找效率。
 *    - 方法：
 *      - 高 16 位保持不变，低 16 位与原本的高 16 位进行异或运算。
 *      - 这样可让哈希分布更均匀，降低哈希冲突率。
 *      <br>
 * 5. 链表树化（转化为红黑树）：
 *    - 当某个桶（bucket）中的链表长度超过 `TREEIFY_THRESHOLD（默认8）` 时，链表会转换为红黑树，提高查找效率。
 *    - 反之，当桶中数据较少时，会退化为链表，减少红黑树维护开销。
 *    <br>
 * 6. 为什么要重写 `equals()` 和 `hashCode()` 方法？
 *    - HashMap 通过 `hashCode()` 计算哈希值，再通过 `equals()` 判断键是否相等。
 *    - 如果只重写 `equals()`，哈希表查找仍需逐个遍历（效率低）。
 *    - 如果只重写 `hashCode()`，不同对象可能会有相同的哈希值（哈希冲突），仍需 `equals()` 进一步确认。
 *    <br>
 * 7. Map 中的 key-value 传递：
 *    - 在 `HashMap` 中，`Node` 的 key 和 value 存储的是对象的引用。
 *    - 因此，若直接修改外部对象的属性值，Map 内部存储的对象也会感知到修改。
 *    <br>
 * ------------------------------------------
 * <br>
 * **励志语录**：
 * 所有的尊重从来都不是别人给予的，而是自己赢来的。
 * 想变强，就必须承受痛苦的磨砺与寂寞的洗礼。
 * **欲戴其冠，必承其重！**
 */
public class StreamFirstFaceDemo {

    public static void main(String[] args) {
        List<Integer> handle = initData();

        // 获取最小值
        int minValue = handle.stream().min(Integer::compareTo).orElseThrow(() -> new RuntimeException("计算错误"));
        System.out.println("Min Value = " + minValue);

        // 获取最大值
        int maxValue = handle.stream().max(Integer::compareTo).orElseThrow(() -> new RuntimeException("计算错误"));
        System.out.println("Max Value = " + maxValue);

        // 连接字符串
        String joinedNumbers = handle.stream().map(String::valueOf).collect(Collectors.joining(", ", "数字是 ", " 结束"));
        System.out.println("Joined Numbers: " + joinedNumbers);

        // 分组处理
        Map<Boolean, Map<String, List<Integer>>> multiLevelGrouping = handle.stream()
                .collect(Collectors.groupingBy(item -> item > 35,
                        Collectors.groupingBy(item -> item > 15 ? "大于15" : "小于等于15")));
        System.out.println("Multi-Level Grouping: " + multiLevelGrouping);

        // TreeMap 示例
        TreeMap<LinkedKey, String> map = new TreeMap<>(Comparator.comparingInt(LinkedKey::hashCode));
        map.put(new LinkedKey("Item1", 1), "Value1");
        map.put(new LinkedKey("Item2", 2), "Value2");
        map.put(new LinkedKey("Item3", 3), "Value3");
        System.out.println("TreeMap: " + map);

        // 反转、打乱、排序
        Collections.reverse(handle);
        System.out.println("Reversed List: " + handle);
        Collections.shuffle(handle);
        System.out.println("Shuffled List: " + handle);
        Collections.sort(handle);
        System.out.println("Sorted List: " + handle);

        System.out.println("Max via Collections: " + Collections.max(handle));
        System.out.println("Min via Collections: " + Collections.min(handle));

        // List 拷贝
        List<Integer> dest = new ArrayList<>(handle);
        System.out.println("Copied List: " + dest);

        // Set 示例
        Set<LinkedKey> set = new TreeSet<>();
        LinkedKey key = new LinkedKey();
        set.add(key);
        set.add(new LinkedKey());
        set.add(new LinkedKey("Test", 100));
        System.out.println("TreeSet: " + set);

        // HashSet 示例
        System.out.println("============= HashSet 示例 =============");
        Set<LinkedKey> hashSet = new HashSet<>();
        LinkedKey l1 = new LinkedKey("AA", 1001);
        LinkedKey l2 = new LinkedKey("BB", 1002);
        hashSet.add(l1);
        hashSet.add(l2);

        // 修改 l1 的 name 可能导致 remove 失败
        l1.setName("CC");
        hashSet.remove(l1);
        System.out.println("HashSet after remove: " + hashSet);

        hashSet.add(new LinkedKey("CC", 1001));
        hashSet.add(new LinkedKey("AA", 1001));
        System.out.println("Final HashSet: " + hashSet);
    }

    private static List<Integer> initData() {
        return new ArrayList<>(List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
                21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50));
    }
}

@ToString
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
class LinkedKey {
    private String name;
    private Integer price;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof LinkedKey)) return false;
        LinkedKey that = (LinkedKey) o;
        return Objects.equals(name, that.name) && Objects.equals(price, that.price);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, price);
    }
}

/*
HashMap笔记：
    在添加的时候OnlyIfAbsent默认为false， 意为只有在当前键值一样时，才会进行覆盖，不然的话就执行putIfAbsent方法，就是只会覆盖空值。
    map中的Node节点，代码
    static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;
        final K key;
        V value;
        Node<K,V> next;

        Node(int hash, K key, V value, Node<K,V> next) {
            this.hash = hash;
            this.key = key;
            this.value = value;
            this.next = next;
        }
    }
    其中的hash就是一开始传递进来的key的值。

    下文是putVal的代码，在进行插入的时候只判断key，两个key的hash必须一致，并且在底层的内存地址相等或者是二者key重写过equals，并且相等。
    换句话说：首先hash值必须相同，同时是同一个对象和内容相同（成立一个即可）
    if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
        e = p;

    高低位扰动算法： 高16位保持不变，低十六位与原本的高十六位进行了异或的运算，从而减少冲突概率。这样子便保留了高位的信息，使参与与运算的部分变得更加具有随机性。
    而且混合后的底位掺杂了高位的部分特征，使高位的信息也被保留了下来。

    单个坑位中的链表的树化（转化为红黑树）：
    剪枝行为解读：就是在当前table节点的树比较小了 那就进行红黑树到链表的转换。

    ***所有的尊重，从来都不是别人给的，而是自己赢来的，要是想变强，承受住痛苦的磨砺与寂寞的洗礼便是第一步。欲戴其冠，必承其重！***
    有志者，事竟成，破釜沉舟，百二秦关终属楚；苦心人，天不负，卧薪尝胆，三千越甲可吞吴；
    變強
    所以提起了一个问题，为什么要重写equals和hashcode方法？
    重写hashcode和equals一半就是为了实现一些合乎情理，切合实际的，在现实生活中经常出现的一些场景（比如两个人姓名与年龄一致，则说明二者equals相等）
    是为了实现这种逻辑的一种实现方法。如果不重写的话，则他在源码中对比的两个对象的地址值。
        那么为什么要同时重写hashcode和equals方法呢？(详情可参考 https://blog.csdn.net/qq_45427600/article/details/113484445)
            只重写equals方法：
            是可以的，但是如果要比较A和B，但是B在集合中，并且集合很大，总不能一直调用equals来比较吧。（效率问题）所以我们就需要了hashcode方法。
            只重写hashcode方法：
            hashcode相等，两个对象不一定相等，还需要equals来进行比较
                hashcode：就是为了更加高效的去比较俩个对象是否相等。
                equals：实现特殊的需求。

   要注意的是在Map中  Node节点的key和value都是传递的引用  所以直接从外界修改对象属性值，则在map中可以感知到。
 */
